#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "modBusRtuMaster.h"
#include <QButtonGroup>
#include <QDebug>
#include <QStringList>
#include <QTimer>
#include <QTime>
#include <QRegularExpression>

#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <cerrno>
#include <cstring>
#include <cmath>
#include <cstdio>

uint16_t current_fsm_status  = 0;
uint16_t current_fsm_state   = 0;
uint16_t current_fsm_error   = 0;
uint16_t current_home_status = 0;
uint16_t current_home_error  = 0;
uint16_t current_z_valid     = 0;

double current_pressure_P1   = 0.0;
double current_pressure_P2   = 0.0;

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
    , testScenarioStep(0)
    , activeRouteNumber(0)
    , _step(0)
    , isRobotBusy(false)
    , isAlignerBusy(false)
    , isControlBoardBusy(false)
    , totalActiveTimeMs(0)
{
    ui->setupUi(this);

    qDebug().noquote() << "\n========================================================";
    qDebug().noquote() << "[" << QTime::currentTime().toString("hh:mm:ss.zzz") << "] СИСТЕМА ЭПОС-ЛОАД: ЗАПУСК ПРИЛОЖЕНИЯ...";
    qDebug().noquote() << "========================================================\n";
    ::fflush(stdout);

    for(int i = 0; i < 4; ++i) fd_matrix[i] = -1;

    slotInitHardwarePorts();

    reconnectTimer = new QTimer(this);
    connect(reconnectTimer, &QTimer::timeout, this, &MainWindow::slotHandleAutoReconnect);
    reconnectTimer->start(2000);

    connect(ui->btnR1minus, &QPushButton::clicked, this, &MainWindow::slotOnPushR1m);
    connect(ui->btnR1plus,  &QPushButton::clicked, this, &MainWindow::slotOnPushR1p);
    connect(ui->btnR2minus, &QPushButton::clicked, this, &MainWindow::slotOnPushR2m);
    connect(ui->btnR2plus,  &QPushButton::clicked, this, &MainWindow::slotOnPushR2p);
    connect(ui->btnR3minus, &QPushButton::clicked, this, &MainWindow::slotOnPushR3m);
    connect(ui->btnR3plus,  &QPushButton::clicked, this, &MainWindow::slotOnPushR3p);

    connect(ui->btnScanPotr,   &QPushButton::clicked, this, &MainWindow::slotInitHardwarePorts);
    connect(ui->btnDeInitERob, &QPushButton::clicked, this, &MainWindow::slotDeInitERob);
    connect(ui->btnZplis,      &QPushButton::clicked, this, &MainWindow::slotZp);
    connect(ui->btnZminus,     &QPushButton::clicked, this, &MainWindow::slotZm);
    connect(ui->btnSafeXY,     &QPushButton::clicked, this, &MainWindow::slotSave);
    connect(ui->btnSafeZ,      &QPushButton::clicked, this, &MainWindow::slotSaveZ);

    connect(ui->btnGetAngle,   &QPushButton::clicked, this, &MainWindow::slotGetAngle);
    connect(ui->btnGetEncoder, &QPushButton::clicked, this, &MainWindow::slotGetEncoder);
    connect(ui->btnGetXY,      &QPushButton::clicked, this, &MainWindow::slotGetXY);
    connect(ui->btnGetPress,   &QPushButton::clicked, this, &MainWindow::slotGetPressure);

    connect(ui->btnAligner,     &QPushButton::clicked, this, &MainWindow::slotAlignStart);
    connect(ui->btnMoveCorrect, &QPushButton::toggled, this, &MainWindow::slotMoveCorrect);
    connect(ui->btnValveOnOff,  &QPushButton::toggled, this, &MainWindow::slotValveOnOff);

    connect(ui->btnBox1Align, &QPushButton::clicked, this, [this]() {
        uint16_t val = static_cast<uint16_t>(0x8100 + ui->spinBox1_Aligner->value());
        sendRobotRegister(MB_MOVE_H, val);
    });

    connect(ui->btnBox2Align, &QPushButton::clicked, this, [this]() {
        uint16_t val = static_cast<uint16_t>(0x8200 + ui->spinBox2_Aligner->value());
        sendRobotRegister(MB_MOVE_H, val);
    });

    connect(ui->btnAlignBox4, &QPushButton::clicked, this, [this]() {
        sendRobotRegister(MB_MOVE_H, 0x8300);
    });

    connect(ui->btnBox4Box1, &QPushButton::clicked, this, [this]() {
        uint16_t val = static_cast<uint16_t>(0x0100 + ui->spinElips_Box1->value());
        sendRobotRegister(MB_MOVE_H, val);
    });

    connect(ui->btnBox4Box2, &QPushButton::clicked, this, [this]() {
        uint16_t val = static_cast<uint16_t>(0x0200 + ui->spinElips_Box2->value());
        sendRobotRegister(MB_MOVE_H, val);
    });

    connect(ui->btnBox1_Elips, &QPushButton::clicked, this, [this]() {
        ui->textEdit->append("ОРИЕНТАТОР -> Смещение X в безопасную зону 2.0 мм...");
        uint16_t raw_x = static_cast<uint16_t>((2.0 * 10) + 32000);
        sendAlignerRegister(PDU_ALIGN_X_TARGET_POS, raw_x);
    });

    connect(ui->btnBox2_Elips, &QPushButton::clicked, this, [this]() {
        ui->textEdit->append("ОРИЕНТАТОР -> Возврат X в механический ноль (0.0 мм)...");
        uint16_t raw_x = static_cast<uint16_t>((0.0 * 10) + 32000);
        sendAlignerRegister(PDU_ALIGN_X_TARGET_POS, raw_x);
    });

    connect(ui->btnInitRob, &QPushButton::clicked, this, [this]() { sendRobotRegister(MB_COMMAND, 1); });
    connect(ui->btnStatus,  &QPushButton::clicked, this, [this]() { sendRobotReadRequest(MB_STATUS_H, 2); });
    connect(ui->btnPark_2,  &QPushButton::clicked, this, [this]() { sendRobotRegister(MB_COMMAND, 8); });

    connect(ui->btnBox1Box2, &QPushButton::clicked, this, &MainWindow::slotP1ERob);
    connect(ui->btnBox2Box1, &QPushButton::clicked, this, &MainWindow::slotP2ERob);

    _group = new QButtonGroup(this);
    _group->setExclusive(true);
    _group->addButton(ui->btnVoid);       _group->addButton(ui->btnPark);
    _group->addButton(ui->btnHomingZ);   _group->addButton(ui->btnInit);
    _group->addButton(ui->btnBox1);       _group->addButton(ui->btnBox1In);
    _group->addButton(ui->btnBox1Out);    _group->addButton(ui->btnBox2);
    _group->addButton(ui->btnBox2In);     _group->addButton(ui->btnBox2Out);
    _group->addButton(ui->btnAlignerIn);  _group->addButton(ui->btnAlignerOut);
    _group->addButton(ui->btnAligner);
    connect(_group, QOverload<QAbstractButton*,bool>::of(&QButtonGroup::buttonToggled), this, &MainWindow::slotMultyBtn);

    QList<QPushButton*> allButtons = this->findChildren<QPushButton*>();
    for (QPushButton* btn : allButtons) {
        if (btn->parent() != ui->groupBox) continue;
        QString text = btn->objectName().toUpper();
        if (text.contains("ELIPS")) {
            connect(btn, &QPushButton::clicked, this, [this, text]() {
                int cmd = 9;
                if (text.contains("IN")) cmd = 10;
                else if (text.contains("OUT")) cmd = 11;
                sendRobotRegister(MB_COMMAND, cmd);
            });
        }
    }

    connect(ui->btn_dXplus,  &QPushButton::clicked, this, &MainWindow::slotOnPush_dXplus);
    connect(ui->btn_dXminus, &QPushButton::clicked, this, &MainWindow::slotOnPush_dXminus);
    connect(ui->btn_dYplus,  &QPushButton::clicked, this, &MainWindow::slotOnPush_dYplus);
    connect(ui->btn_dYminus, &QPushButton::clicked, this, &MainWindow::slotOnPush_dYminus);

    ui->BoxLoad->clear(); ui->BoxLoad->addItems(QStringList() << "0" << "100" << "500" << "1000" << "2000" << "3000" << "5000" << "10000");
    connect(ui->BoxLoad, &QComboBox::currentTextChanged, this, &MainWindow::slotCbxStep);

    ui->cbxDeltaXY->clear(); ui->cbxDeltaXY->addItems(QStringList() << "0" << "0.01" << "0.05" << "0.1" << "0.5" << "1");
    connect(ui->cbxDeltaXY, &QComboBox::currentTextChanged, this, &MainWindow::slotCbxStepXY);

    ui->cbxDeltaZ->clear(); ui->cbxDeltaZ->addItems(QStringList() << "0" << "10" << "100" << "1000" << "5000" << "10000");
    connect(ui->cbxDeltaZ, &QComboBox::currentTextChanged, this, &MainWindow::slotCbxStepZ);

    _listButtons.append(ui->btnR1minus); _listButtons.append(ui->btnR2minus); _listButtons.append(ui->btnR3minus);
    _listButtons.append(ui->btnR1plus);  _listButtons.append(ui->btnR2plus);  _listButtons.append(ui->btnR3plus);

    timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, &MainWindow::slotTimer);
    timer->start(250);

    uiRefreshTimer = new QTimer(this);
    connect(uiRefreshTimer, &QTimer::timeout, this, [this]() {
        if (isAnyModuleWorking()) {
            qint64 currentSession = workElapsedTimer.elapsed();
            double seconds = (totalActiveTimeMs + currentSession) / 1000.0;
            ui->textEdit->setText(QString("Чистое время работы системы: %1 сек. [АКТИВНО]").arg(seconds, 0, 'f', 2));
        }
    });
    connect(uiRefreshTimer, &QTimer::timeout, this, &MainWindow::ReadFromPorts);
    uiRefreshTimer->start(50);

    // Безопасный автозапуск технологической линии (Выдержка 16 секунд для завершения хоминга осей платы)
    QTimer::singleShot(16000, this, [this]() {
        qDebug().noquote() << "\n⏱️ [ТАЙМЕР АВТОЗАПУСКА] Оборудование откалибровано. Передача управления на Сценарий №1...";
        ::fflush(stdout);
        this->slotP1ERob();
    });
}

MainWindow::~MainWindow() {
    for(int i = 0; i < 4; ++i) { if (fd_matrix[i] >= 0) ::close(fd_matrix[i]); }
    delete ui;
}

void MainWindow::updateWorkTimerLogic() {
    static bool wasWorking = false;
    bool isWorkingNow = isAnyModuleWorking();
    if (isWorkingNow && !wasWorking) {
        workElapsedTimer.start();
        wasWorking = true;
    }
    else if (!isWorkingNow && wasWorking) {
        totalActiveTimeMs += workElapsedTimer.elapsed();
        wasWorking = false;
    }
}

quint16 MainWindow::calculateCRC(const QByteArray &data) {
    // Исправлено: Вместо математического сдвига используем проверенный табличный метод Modbus RTU
    ModbusRTUMaster mb;
    return mb.RC_crc16_2(const_cast<QByteArray*>(&data));
}

bool MainWindow::configureTermios(int fd) {
    struct termios tty;
    if (tcgetattr(fd, &tty) != 0) return false;
    cfsetospeed(&tty, B115200); cfsetispeed(&tty, B115200);
    tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
    tty.c_iflag &= ~IGNBRK; tty.c_lflag = 0; tty.c_oflag = 0;
    tty.c_cc[VMIN] = 0; tty.c_cc[VTIME] = 0;
    tty.c_iflag &= ~(IXON | IXOFF | IXANY);
    tty.c_cflag |= (CLOCAL | CREAD);
    tty.c_cflag &= ~(PARENB | PARODD); tty.c_cflag &= ~CSTOPB; tty.c_cflag &= ~CRTSCTS;
    return (tcsetattr(fd, TCSANOW, &tty) == 0);
}

void MainWindow::slotInitHardwarePorts() {
QStringList paths; paths << "/dev/ttyS0" << "/dev/ttyS1" << "/dev/ttyS2" << "/dev/ttyS3";
qDebug().noquote() << "\n⚙️ [" << QTime::currentTime().toString("hh:mm:ss.zzz") << "] ИНИЦИАЛИЗАЦИЯ ПОРТОВ RS-485:";
for (int i = 0; i < 4; ++i) {
if (fd_matrix[i] >= 0) {
::close(fd_matrix[i]);
fd_matrix[i] = -1;
}
fd_matrix[i] = ::open(paths[i].toLocal8Bit().constData(), O_RDWR | O_NOCTTY | O_NONBLOCK);
QString portLabel = (i == 0) ? "ttyS0 [РОБОТ]" : (i == 1) ? "ttyS1 [ВЫРАВНИВАТЕЛЬ]" : (i == 2) ? "ttyS2 [ЦУ]" : "ttyS3 [РЕЗЕРВ]";
if (fd_matrix[i] < 0) {
QString errStr = QString(" ❌ %1: СБОЙ ОТКРЫТИЯ. Код: %2 (%3)").arg(portLabel).arg(errno).arg(std::strerror(errno));
qDebug().noquote() << errStr; ui->textEdit->append(errStr);
} else {
if (configureTermios(fd_matrix[i])) {
QString successStr = QString(" ✅ %1: ОТКРЫТ УСПЕШНО (115200 8N1). Дескриптор: %2").arg(portLabel).arg(fd_matrix[i]);
qDebug().noquote() << successStr; ui->textEdit->append(successStr);
} else {
QString termiosErr = QString(" ❌ %1: Ошибка конфигурации termios.").arg(portLabel);
qDebug().noquote() << termiosErr; ui->textEdit->append(termiosErr);
::close(fd_matrix[i]); fd_matrix[i] = -1;
}
}
}
qDebug().noquote() << "-----------------------------------------------------\n";
::fflush(stdout);
}
void MainWindow::slotHandleAutoReconnect() {
QStringList paths; paths << "/dev/ttyS0" << "/dev/ttyS1" << "/dev/ttyS2" << "/dev/ttyS3";
for (int i = 0; i < 4; ++i) {
if (fd_matrix[i] < 0) {
int tryFd = ::open(paths[i].toLocal8Bit().constData(), O_RDWR | O_NOCTTY | O_NONBLOCK);
if (tryFd >= 0) {
if (configureTermios(tryFd)) {
fd_matrix[i] = tryFd;
QString portLabel = (i == 0) ? "РОБОТ ttyS0" : (i == 1) ? "ВЫРАВНИВАТЕЛЬ ttyS1" : "ЦУ ttyS2";
qDebug().noquote() << "🔄 Автореконнект: Связь с" << portLabel << "восстановлена.";
::fflush(stdout);
} else ::close(tryFd);
}
}
}
}
void MainWindow::sendRobotRegister(quint16 reg, quint16 value) {
Q_UNUSED(reg);
if (fd_matrix[EBOX_PORT_ROBOT] < 0) return;
QByteArray packet;
packet.append((char)0x12);
packet.append((char)0x06);
packet.append((char)0x00); packet.append((char)0x03); // MB_COMMAND = 3
packet.append((char)((value >> 8) & 0xFF)); packet.append((char)(value & 0xFF));
quint16 crc_val = calculateCRC(packet);
packet.append((char)(crc_val & 0xFF)); packet.append((char)((crc_val >> 8) & 0xFF));
isRobotBusy = false;
updateWorkTimerLogic();
qDebug().noquote() << "📡 TX -> РОБОТ (ttyS0):" << packet.toHex(' ').toUpper();
::fflush(stdout);
::write(fd_matrix[EBOX_PORT_ROBOT], packet.constData(), packet.size());
}
void MainWindow::sendAlignerRegister(quint16 reg, quint16 value) {
if (fd_matrix[EBOX_PORT_ALIGNER] < 0) return;
QByteArray packet;
packet.append((char)0x01); packet.append((char)0x06); // Только FC06 поддерживается платой
packet.append((char)((reg >> 8) & 0xFF)); packet.append((char)(reg & 0xFF));
packet.append((char)((value >> 8) & 0xFF)); packet.append((char)(value & 0xFF));
quint16 crc_val = calculateCRC(packet);
packet.append((char)(crc_val & 0xFF)); packet.append((char)((crc_val >> 8) & 0xFF));
usleep(4000); // 4 мс тишины в шине (паспортное требование)
qDebug().noquote() << "📡 TX -> ВЫРАВНИВАТЕЛЬ (ttyS1):" << packet.toHex(' ').toUpper();
::fflush(stdout);
::write(fd_matrix[EBOX_PORT_ALIGNER], packet.constData(), packet.size());
}
void MainWindow::sendRobotReadRequest(quint16 reg, quint16 count) {
if (fd_matrix[EBOX_PORT_ROBOT] < 0) return;
QByteArray packet;
packet.append((char)0x12);
packet.append((char)0x03);
packet.append((char)((reg >> 8) & 0xFF)); packet.append((char)(reg & 0xFF));
packet.append((char)((count >> 8) & 0xFF)); packet.append((char)(count & 0xFF));
quint16 crc_val = calculateCRC(packet);
packet.append((char)(crc_val & 0xFF)); packet.append((char)((crc_val >> 8) & 0xFF));
isRobotBusy = false;
::write(fd_matrix[EBOX_PORT_ROBOT], packet.constData(), packet.size());
}
void MainWindow::parseRobotResponse(const QByteArray &packet) {
if (packet.size() < 5) return;
if (static_cast<uint8_t>(packet.at(1)) == 0x03) {
uint8_t byteCount = static_cast<uint8_t>(packet.at(2));
if ((byteCount == 4 || byteCount == 2) && packet.size() >= 7) {
uint16_t robotStatus = (static_cast<uint8_t>(packet.at(3)) << 8) | static_cast<uint8_t>(packet.at(4));
isRobotBusy = !(robotStatus == 0 || robotStatus == 29);
updateWorkTimerLogic();
}
}
}
void MainWindow::slotP1ERob() {
isRobotBusy = false; isAlignerBusy = false;
current_fsm_status = 2; // Принудительный сброс READY для каскада
if (ui->spinBox1Take->value() < 1 || ui->spinBox1Take->value() > 4) ui->spinBox1Take->setValue(1);
qDebug().noquote() << "\n▶️ СТАРТ: Сценарий №1 (BOX1 -> Aligner -> Elips)";
::fflush(stdout);
activeRouteNumber = 1; testScenarioStep = 1;
runRoute1Machine();
}
void MainWindow::slotP2ERob() {
isRobotBusy = false; isAlignerBusy = false;
current_fsm_status = 2;
if (ui->spinBox2Take->value() < 1 || ui->spinBox2Take->value() > 4) ui->spinBox2Take->setValue(1);
qDebug().noquote() << "\n▶️ СТАРТ: Сценарий №2 (BOX2 -> Aligner -> Elips)";
::fflush(stdout);
activeRouteNumber = 2; testScenarioStep = 1;
runRoute2Machine();
}
void MainWindow::runRoute1Machine() {
if (testScenarioStep == -1) return;
switch (testScenarioStep) {
case 1: { // Шаг 1: Захват пластины из BOX1 и перемещение на Aligner
int slot = ui->spinBox1Take->value();
if (slot < 1 || slot > 4) slot = 1;
qDebug().noquote() << "╠ [Маршрут 1][Шаг 1] Перенос: BOX1 (слот" << slot << ") >> ALIGNER";
sendRobotRegister(MB_COMMAND, 2);
testScenarioStep = -1;
QTimer::singleShot(1500, this, this, slot {
QByteArray packet;
packet.append((char)0x12); packet.append((char)0x10);
packet.append((char)0x00); packet.append((char)0x18);
packet.append((char)0x00); packet.append((char)0x02);
packet.append((char)0x04);
packet.append((char)0x81); packet.append((char)slot);
packet.append((char)0x03); packet.append((char)0x00);
quint16 crc_val = this->calculateCRC(packet);
packet.append((char)(crc_val & 0xFF)); packet.append((char)((crc_val >> 8) & 0xFF));
if (this->fd_matrix[EBOX_PORT_ROBOT] >= 0) {
::write(this->fd_matrix[EBOX_PORT_ROBOT], packet.constData(), packet.size());
}
QTimer::singleShot(7500, this, this {
this->testScenarioStep = 2;
this->runRoute1Machine();
});
});
break;
}
case 2: { // Шаг 2: Накатка геометрии Z и запуск ПОЛНОГО цикла выравнивания стола (125 сек)
qDebug().noquote() << "╠ [Маршрут 1][Шаг 2] Накатка геометрии Z и запуск Notch-поиска выравнивателя...";
testScenarioStep = -1;
// Задаем рабочие лимиты Z (иначе FSM выдаст аппаратную ошибку 9)
this->sendAlignerRegister(PDU_Z_WORK_DOWN, 27); usleep(6000);
this->sendAlignerRegister(PDU_Z_WORK_UP, 650); usleep(6000);
// Игнорируем физические датчики присосок: принудительно взводим GPIO насоса стола
this->sendAlignerRegister(PDU_ALIGN_PUMP_ENABLE, 1);
QTimer::singleShot(500, this, this {
this->sendAlignerRegister(PDU_ALIGN_FSM_RESET, 1);
QTimer::singleShot(500, this, this {
qDebug().noquote() << "🚀 [ВЫРАВНИВАТЕЛЬ] Сканирование запущено. Физическое ожидание оборота стола (125 сек)...";
this->sendAlignerRegister(PDU_START_ALIGN, 1); // Команда 1 — Полное штатное центрирование
// ЖЕСТКИЙ БЕЗОПАСНЫЙ ТАЙМИНГ: Оборот стола занимает ~120 сек. Задаем 125 секунд.
QTimer::singleShot(125000, this, this {
qDebug().noquote() << "✅ [ВЫРАВНИВАТЕЛЬ] Физический цикл вращения гарантированно завершен.";
this->testScenarioStep = 3;
this->runRoute1Machine();
});
});
});
break;
}
case 3: { // Шаг 3: Перенос пластины со стола Aligner >> Elips
qDebug().noquote() << "╠ [Маршрут 1][Шаг 3] Забор пластины. Перенос: ALIGNER >> ELIPS";
testScenarioStep = -1;
// Отключаем насос стола, снимая виртуальный вакуум
this->sendAlignerRegister(PDU_ALIGN_PUMP_ENABLE, 0); usleep(6000);
QByteArray packet;
packet.append((char)0x12); packet.append((char)0x10);
packet.append((char)0x00); packet.append((char)0x18);
packet.append((char)0x00); packet.append((char)0x02);
packet.append((char)0x04);
packet.append((char)0x83); packet.append((char)0x00);
packet.append((char)0x04); packet.append((char)0x00);
quint16 crc_val = calculateCRC(packet);
packet.append((char)(crc_val & 0xFF)); packet.append((char)((crc_val >> 8) & 0xFF));
if (fd_matrix[EBOX_PORT_ROBOT] >= 0) {
::write(fd_matrix[EBOX_PORT_ROBOT], packet.constData(), packet.size());
}
QTimer::singleShot(7500, this, this {
this->testScenarioStep = 4;
this->runRoute1Machine();
});
break;
}
case 4: { // Шаг 4: Увод манипулятора в PARK
qDebug().noquote() << "╠ [Маршрут 1][Шаг 4] Разгрузка на Elips завершена. Увод руки робота в PARK...";
sendRobotRegister(MB_COMMAND, 8);
testScenarioStep = -1;
QTimer::singleShot(5000, this, this {
this->testScenarioStep = 5;
this->runRoute1Machine();
});
break;
}
case 5: { // Шаг 5: Окончание Маршрута 1 и каскадный переход к Маршруту 2
qDebug().noquote() << "🏁 [МАРШРУТ 1] Завершен. Пауза перед Сценарием №2 (5 сек)...";
testScenarioStep = 0; activeRouteNumber = 0;
QTimer::singleShot(5000, this, this {
this->slotP2ERob();
});
break;
}
}
}
void MainWindow::runRoute2Machine() {
if (testScenarioStep == -1) return;
switch (testScenarioStep) {
case 1: { // Шаг 1: Захват из BOX2 и перенос на Aligner
int slot = ui->spinBox2Take->value();
if (slot < 1 || slot > 4) slot = 1;
qDebug().noquote() << "╠ [Маршрут 2][Шаг 1] Перенос: BOX2 (слот" << slot << ") >> ALIGNER";
sendRobotRegister(MB_COMMAND, 2);
testScenarioStep = -1;
QTimer::singleShot(1500, this, this, slot {
QByteArray packet;
packet.append((char)0x12); packet.append((char)0x10);
packet.append((char)0x00); packet.append((char)0x18);
packet.append((char)0x00); packet.append((char)0x02);
packet.append((char)0x04);
packet.append((char)0x82); packet.append((char)slot);
packet.append((char)0x03); packet.append((char)0x00);
quint16 crc_val = this->calculateCRC(packet);
packet.append((char)(crc_val & 0xFF)); packet.append((char)((crc_val >> 8) & 0xFF));
if (this->fd_matrix[EBOX_PORT_ROBOT] >= 0) {
::write(this->fd_matrix[EBOX_PORT_ROBOT], packet.constData(), packet.size());
}
QTimer::singleShot(7500, this, this {
this->testScenarioStep = 2;
this->runRoute2Machine();
});
});
break;
}
case 2: { // Шаг 2: Полный цикл выравнивания стола для Маршрута 2 (125 сек)
qDebug().noquote() << "╠ [Маршрут 2][Шаг 2] Накатка геометрии Z и запуск Notch-поиска выравнивателя...";
testScenarioStep = -1;
this->sendAlignerRegister(PDU_Z_WORK_DOWN, 27); usleep(6000);
this->sendAlignerRegister(PDU_Z_WORK_UP, 650); usleep(6000);
this->sendAlignerRegister(PDU_ALIGN_PUMP_ENABLE, 1);
QTimer::singleShot(500, this, this {
this->sendAlignerRegister(PDU_ALIGN_FSM_RESET, 1);
QTimer::singleShot(500, this, this {
qDebug().noquote() << "🚀 [ВЫРАВНИВАТЕЛЬ] Сканирование запущено. Физическое ожидание оборота стола (125 сек)...";
this->sendAlignerRegister(PDU_START_ALIGN, 1);
QTimer::singleShot(125000, this, this {
this->testScenarioStep = 3;
this->runRoute2Machine();
});
});
});
break;
}
case 3: { // Шаг 3: Перенос Aligner >> Elips
qDebug().noquote() << "╠ [Маршрут 2][Шаг 3] Забор пластины. Перенос: ALIGNER >> ELIPS";
testScenarioStep = -1;
this->sendAlignerRegister(PDU_ALIGN_PUMP_ENABLE, 0); usleep(6000);
QByteArray packet;
packet.append((char)0x12); packet.append((char)0x10);
packet.append((char)0x00); packet.append((char)0x18);
packet.append((char)0x00); packet.append((char)0x02);
packet.append((char)0x04);
packet.append((char)0x83); packet.append((char)0x00);
packet.append((char)0x04); packet.append((char)0x00);
quint16 crc_val = calculateCRC(packet);
packet.append((char)(crc_val & 0xFF)); packet.append((char)((crc_val >> 8) & 0xFF));
if (fd_matrix[EBOX_PORT_ROBOT] >= 0) {
::write(fd_matrix[EBOX_PORT_ROBOT], packet.constData(), packet.size());
}
QTimer::singleShot(7500, this, this {
this->testScenarioStep = 4;
this->runRoute2Machine();
});
break;
}
case 4: { // Шаг 4: Финальный увод руки в PARK
qDebug().noquote() << "╠ [Маршрут 2][Шаг 4] Отвод манипулятора в PARK...";
sendRobotRegister(MB_COMMAND, 8);
testScenarioStep = -1;
QTimer::singleShot(5000, this, this {
this->testScenarioStep = 5;
this->runRoute2Machine();
});
break;
}
case 5: { // Шаг 5: Финал сквозного сценария
qDebug().noquote() << "🏁 ==========================================================================";
qDebug().noquote() << "🏁 СЦЕНАРНАЯ ЦЕПОЧКА (МАРШРУТ 1 + МАРШРУТ 2) УСПЕШНО ВЫПОЛНЕНА ПО ТАЙМИНГАМ!";
qDebug().noquote() << "🏁 ==========================================================================";
testScenarioStep = 0; activeRouteNumber = 0;
updateWorkTimerLogic();
break;
}
}
}
void MainWindow::ReadFromPorts() {
static QList portBuffers = { QByteArray(), QByteArray(), QByteArray(), QByteArray() };
char readBuffer;
for (int i = 0; i < 4; ++i) {
if (fd_matrix[i] < 0) continue;
fd_set read_fds;
struct timeval timeout = {0, 0};
FD_ZERO(&read_fds);
FD_SET(fd_matrix[i], &read_fds);
while (::select(fd_matrix[i] + 1, &read_fds, nullptr, nullptr, &timeout) > 0) {
ssize_t bytesRead = ::read(fd_matrix[i], &readBuffer, 1);
if (bytesRead > 0) portBuffers[i].append(readBuffer);
else break;
}
QByteArray &data = portBuffers[i];
if (data.isEmpty()) continue;
if (i == EBOX_PORT_ALIGNER) {
if (data.size() >= 5) {
uint8_t fCode = static_cast<uint8_t>(data.at(1));
if (fCode == 0x03) {
uint8_t byteCount = static_cast<uint8_t>(data.at(2));
int packetSize = 3 + byteCount + 2;
if (data.size() >= packetSize) {
QByteArray fullPacket = data.left(packetSize);
data.remove(0, packetSize);
if (byteCount == 6 && fullPacket.size() >= 11) {
current_fsm_status = (static_cast<uint8_t>(fullPacket.at(3)) << 8) | static_cast<uint8_t>(fullPacket.at(4));
current_fsm_state = (static_cast<uint8_t>(fullPacket.at(5)) << 8) | static_cast<uint8_t>(fullPacket.at(6));
current_fsm_error = (static_cast<uint8_t>(fullPacket.at(7)) << 8) | static_cast<uint8_t>(fullPacket.at(8));
}
if (byteCount == 30 && fullPacket.size() >= 35) {
uint16_t rawX = (static_cast<uint8_t>(fullPacket.at(3)) << 8) | static_cast<uint8_t>(fullPacket.at(4));
double posX = (static_cast(rawX) - 32000.0) / 10.0;
uint16_t rawR = (static_cast<uint8_t>(fullPacket.at(13)) << 8) | static_cast<uint8_t>(fullPacket.at(14));
double posR = static_cast(rawR) / 10.0;
ui->textEdit->append(QString("ТЕЛЕМЕТРИЯ -> X: %1 мм, R: %2°").arg(posX, 0, 'f', 1).arg(posR, 0, 'f', 1));
}
}
}
else if (fCode == 0x06) {
if (data.size() >= 8) data.remove(0, 8);
}
else { data.remove(0, 1); }
}
if (data.size() > 200) data.clear();
}
if (i == EBOX_PORT_ROBOT && data.size() >= 5) {
uint8_t fCode = static_cast<uint8_t>(data.at(1));
if (fCode == 0x03) {
uint8_t byteCount = static_cast<uint8_t>(data.at(2));
int packetSize = 3 + byteCount + 2;
if (data.size() >= packetSize) {
QByteArray fullPacket = data.left(packetSize);
data.remove(0, packetSize);
parseRobotResponse(fullPacket);
}
} else { data.remove(0, 1); }
if (data.size() > 200) data.clear();
}
}
}
void MainWindow::slotTimer() {
static int alignerInitState = 0;
static int internalTestingStage = 0;
static int delayCounter = 0;
// Исправлено: Старые проверщики шага 22 закомментированы полностью, чтобы не конфликтовать со сквозным циклом времени.
/*
if (testScenarioStep == 22) { ... }
*/
if (fd_matrix[EBOX_PORT_ALIGNER] >= 0 && delayCounter == 0) {
QByteArray alignerPacket;
alignerPacket.append((char)0x01); alignerPacket.append((char)0x03);
if (internalTestingStage == 0) {
alignerPacket.append((char)0x00); alignerPacket.append((char)79);
alignerPacket.append((char)0x00); alignerPacket.append((char)3);
} else {
alignerPacket.append((char)0x00); alignerPacket.append((char)30);
alignerPacket.append((char)0x00); alignerPacket.append((char)15);
}
quint16 crc_val = calculateCRC(alignerPacket);
alignerPacket.append((char)(crc_val & 0xFF)); alignerPacket.append((char)((crc_val >> 8) & 0xFF));
usleep(5000);
::write(fd_matrix[EBOX_PORT_ALIGNER], alignerPacket.constData(), alignerPacket.size());
}
// Автомат инициализации и хоминга выравнивателя
if (internalTestingStage == 0) {
if (delayCounter > 0) {
delayCounter--;
return;
}
switch (alignerInitState) {
case 0:
qDebug().noquote() << "🔄 [ХОМИНГ ВЫРАВНИВАТЕЛЬ] Шаг 1: Сброс вакуума. Насос OFF.";
sendAlignerRegister(PDU_ALIGN_PUMP_ENABLE, 0);
alignerInitState = 1; delayCounter = 2;
break;
case 1:
qDebug().noquote() << "🔄 [ХОМИНГ ВЫРАВНИВАТЕЛЬ] Шаг 2: Маска вертикальной оси Z (PDU 83 = 4).";
sendAlignerRegister(PDU_REG_HOME_AXES, 4);
alignerInitState = 2; delayCounter = 2;
break;
case 2:
qDebug().noquote() << "🔄 [ХОМИНГ ВЫРАВНИВАТЕЛЬ] Шаг 3: Пуск REG_HOME_COMMAND. Штыри Z едут вниз...";
sendAlignerRegister(PDU_REG_HOME_COMMAND, 1);
alignerInitState = 3; delayCounter = 16;
break;
case 3:
qDebug().noquote() << "🔄 [ХОМИНГ ВЫРАВНИВАТЕЛЬ] Шаг 4: Z базирована. Запись лимита Z_WORK_DOWN (390).";
sendAlignerRegister(PDU_Z_WORK_DOWN, 27);
alignerInitState = 4; delayCounter = 2;
break;
case 4:
qDebug().noquote() << "🔄 [ХОМИНГ ВЫРАВНИВАТЕЛЬ] Шаг 5: Запись лимита Z_WORK_UP (391).";
sendAlignerRegister(PDU_Z_WORK_UP, 650);
alignerInitState = 5; delayCounter = 2;
break;
case 5:
qDebug().noquote() << "🔄 [ХОМИНГ ВЫРАВНИВАТЕЛЬ] Шаг 6: Маска плоскости стола X+R (PDU 83 = 3) и пуск.";
sendAlignerRegister(PDU_REG_HOME_AXES, 3); ::usleep(40000);
sendAlignerRegister(PDU_REG_HOME_COMMAND, 1);
alignerInitState = 6; delayCounter = 24;
break;
case 6:
qDebug().noquote() << "🔄 [ХОМИНГ ВЫРАВНИВАТЕЛЬ] Шаг 7: Калибровка окончена. Перевод выравнивателя в READY.";
ui->textEdit->append("[АВТОМАТ ИНИЦИАЛИЗАЦИИ] ТЕСТ ОПТИКИ И КАЛИБРОВКА ЗАВЕРШЕНЫ. СИСТЕМА ГОТОВА.");
current_fsm_status = 2;
isAlignerBusy = false;
internalTestingStage = 1;
alignerInitState = 7;
break;
}
}
ReadFromPorts();
}
void MainWindow::slotGetAngle() { sendRobotReadRequest(4, 6); }
void MainWindow::slotGetEncoder() { sendRobotReadRequest(10, 6); }
void MainWindow::slotGetXY() { sendRobotReadRequest(16, 6); }
void MainWindow::slotGetPressure() { sendRobotReadRequest(21, 4); }
void MainWindow::slotZp() { sendRobotRegister(MB_COMMAND, 28); }
void MainWindow::slotZm() { sendRobotRegister(MB_COMMAND, 29); }
void MainWindow::slotCbxStep() {
QString str = ui->BoxLoad->currentText(); _step = str.toInt();
ui->btnR1minus->setText("-" + str); ui->btnR2minus->setText("-" + str); ui->btnR3minus->setText("-" + str);
ui->btnR1plus->setText("+" + str); ui->btnR2plus->setText("+" + str); ui->btnR3plus->setText("+" + str);
sendRobotRegister(MB_SET_delta_ENC, _step);
}
void MainWindow::slotCbxStepXY() {
sendRobotRegister(MB_SET_delta_XY_H, static_cast(ui->cbxDeltaXY->currentText().toDouble() * 100));
}
void MainWindow::slotCbxStepZ() { sendRobotRegister(MB_SET_delta_Z, ui->cbxDeltaZ->currentText().toInt()); }
void MainWindow::slotOnPush_dXplus() { sendRobotRegister(MB_COMMAND, 24); }
void MainWindow::slotOnPush_dXminus() { sendRobotRegister(MB_COMMAND, 25); }
void MainWindow::slotOnPush_dYplus() { sendRobotRegister(MB_COMMAND, 26); }
void MainWindow::slotOnPush_dYminus() { sendRobotRegister(MB_COMMAND, 27); }
void MainWindow::slotMultyBtn(QAbstractButton* button, bool checked) {
if (!button || !checked) return;
QString name = button->objectName();
int commandId = 0;
if (name == "btnPark") commandId = 8;
else if (name == "btnHomingZ") commandId = 5;
else if (name == "btnInit") commandId = 2;
else if (name == "btnBox1") commandId = 18;
else if (name == "btnBox1In") commandId = 19;
else if (name == "btnBox1Out") commandId = 20;
else if (name == "btnBox2") commandId = 15;
else if (name == "btnBox2In") commandId = 16;
else if (name == "btnBox2Out") commandId = 17;
else if (name == "btnAligner") commandId = 12;
else if (name == "btnAlignerIn") commandId = 13;
else if (name == "btnAlignerOut") commandId = 14;
if (commandId > 0) sendRobotRegister(MB_COMMAND, commandId);
}
void MainWindow::slotValveOnOff(bool toggled) {
ui->btnValveOnOff->setText(toggled ? "VacuumON" : "VacuumOFF");
sendRobotRegister(MB_COMMAND, toggled ? 22 : 23);
}
void MainWindow::slotDeInitERob() { sendRobotRegister(MB_COMMAND, 3); }
void MainWindow::slotSave() { sendRobotRegister(MB_COMMAND, 36); }
void MainWindow::slotSaveZ() { sendRobotRegister(MB_COMMAND, 38); }
void MainWindow::slotOnPushR1p() { sendRobotRegister(0, 105); }
void MainWindow::slotOnPushR1m() { sendRobotRegister(0, 106); }
void MainWindow::slotOnPushR2p() { sendRobotRegister(0, 107); }
void MainWindow::slotOnPushR2m() { sendRobotRegister(0, 108); }
void MainWindow::slotOnPushR3p() { sendRobotRegister(0, 109); }
void MainWindow::slotOnPushR3m() { sendRobotRegister(0, 110); }
void MainWindow::slotAlignStart() { sendAlignerRegister(PDU_START_ALIGN, 1); }
void MainWindow::slotAlignStop() { sendAlignerRegister(PDU_ALIGN_FSM_STOP, 1); }
void MainWindow::slotAlignPumpOnOff(bool toggled) { sendAlignerRegister(PDU_ALIGN_PUMP_ENABLE, toggled ? 1 : 0); }
void MainWindow::slotMoveCorrect(bool toggled) { Q_UNUSED(toggled); }
void MainWindow::slotP3ERob() {} void MainWindow::slotP4ERob() {} void MainWindow::slotP5ERob() {}
void MainWindow::slotOnPushLoadEnc() {} void MainWindow::slotParkERob(bool checked) { Q_UNUSED(checked); }
void MainWindow::slotCbxOpenPort() {} void MainWindow::slotAlignFactoryHome() {} void MainWindow::slotHandleEllipsometerNetworkData() {}
void MainWindow::stopAllSystemsEmergency(const QString &reason) { Q_UNUSED(reason); } void MainWindow::slotAdvancedEposMachine() {}
